/*=============================================================================
PostProcessHud3D : 3D hud post processing
Copyright (c) 2001 Crytek Studios. All Rights Reserved.

Revision history:
* 23/02/2005: Re-factored/Converted to CryEngine 2.0 by Tiago Sousa
* Created by Tiago Sousa

=============================================================================*/

#include "StdAfx.h"
#include "DriverD3D.h"
#include "I3DEngine.h"
#include "D3DPostProcess.h"
#include "D3DStereo.h"
#include <IFlashUI.h>
#include "RenderObjectDefines.h"

#pragma warning(disable: 4244)

enum EHud3dDebugView
{
	eHUD3D_DEBUG_VIEW_Off = 0,
	eHUD3D_DEBUG_VIEW_SolidFill,
	eHUD3D_DEBUG_VIEW_WireFrame,
	eHUD3D_DEBUG_VIEW_UnwrapOnToFlashTex
};

int16 C3DHud::SHudData::s_nFlashWidthMax = 0;
int16 C3DHud::SHudData::s_nFlashHeightMax = 0;

void C3DHud::SHudData::Init()
{
	Release();

	nSortVal = nFlashWidth = nFlashHeight = 0;
	pFlashPlayer = NULL;
	bVisible = true;

	pDiffuse = pShaderResources->GetTexture(EFTT_DIFFUSE);
	assert( pDiffuse );

	IDynTextureSource* pDynTexSrc = pDiffuse ? pDiffuse->m_Sampler.m_pDynTexSource : NULL;
	if (pDynTexSrc)
	{
		IUIElement *pUIElement = (IUIElement *) pDynTexSrc->GetSource(IDynTextureSource::DTS_I_UIELEMENT);
		if (pUIElement)
		{
			pFlashPlayer = pUIElement->GetFlashPlayerThreadSafe(); // will also call pFlashPlayer->AddRef()!
			bVisible = pUIElement->IsVisible();
		}
		else
		{
			pFlashPlayer = (IFlashPlayer*) pDynTexSrc->GetSource(IDynTextureSource::DTS_I_FLASHPLAYER);
			if (pFlashPlayer)
				pFlashPlayer->AddRef();
			bVisible = pFlashPlayer != NULL;
		}
		if (pFlashPlayer)
		{
			nFlashWidth = pFlashPlayer->GetWidth();
			nFlashHeight = pFlashPlayer->GetHeight();

			s_nFlashWidthMax = max(nFlashWidth, s_nFlashWidthMax);
			s_nFlashHeightMax = max(nFlashHeight, s_nFlashHeightMax);
		}
	}

	if (CRenderer::CV_r_PostProcessHUD3D != 2)
		nSortVal = nFlashWidth * nFlashHeight; // we sort by size
	else
	{
		int nResID =  pShaderResources->m_Id;
		CShader *pShader = (CShader *)pShaderItem->m_pShader;
		nSortVal = (nResID<<18) | (pShader->mfGetID()<<6) | (pShaderItem->m_nTechnique&0x3f);
	}
}

void C3DHud::SHudData::Release()
{
 	SAFE_RELEASE(pFlashPlayer);
}
////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////

int C3DHud::CreateResources()
{
	SAFE_RELEASE( m_pNoise );

	m_pNoise = CTexture::ForName("EngineAssets/Textures/vector_noise.dds",  FT_DONT_RESIZE | FT_DONT_STREAM, eTF_Unknown);

	return true;
}

////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////

void C3DHud::Release()
{
	SAFE_RELEASE( m_pNoise );

	for(uint32 i=0; i<RT_COMMAND_BUF_COUNT; ++i)
		stl::free_container(m_pRenderData[i]);
}

////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////

bool C3DHud::Preprocess()
{
	const bool bForceRefresh = (m_pOverideCacheDelay->GetParam() > 0.5f);
	if(bForceRefresh)
	{
		return true;
	}

	const uint32 nThreadID = gRenDev->m_RP.m_nProcessThreadID;
	if( m_pRenderData[nThreadID].empty() )
	{
		return false;
	}

	return true;
}

////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////

void C3DHud::Reset(bool bOnSpecChange)
{
	for(uint32 i=0; i<RT_COMMAND_BUF_COUNT; ++i)
		m_pRenderData[i].resize(0);
	m_pOpacityMul->ResetParam(1.0f);
	m_pHudColor->ResetParamVec4(Vec4(1.0f, 1.0f, 1.0f, 1.0f));
	m_pGlowMul->ResetParam(1.0f);
	m_pChromaShift->ResetParam(0.0f);
	m_pDofMultiplier->ResetParam(1.0f);
	m_maxParallax = 0.0f;
	m_interferenceRandTimer = 0.0f;
	m_pOverideCacheDelay->ResetParam(0);

	// x= hudItemOverrideStrength, y= interferenceRandFrequency, z= dofInterferenceStrength, w = free
	m_pInterferenceParams[0]->ResetParamVec4(Vec4(1.0f, 0.055f, 0.8f, 0.0f));

	// x = randomGrainStrengthScale, y= randomFadeStrengthScale, z= chromaShiftStrength, w= chromaShiftDist
	m_pInterferenceParams[1]->ResetParamVec4(Vec4(0.3f, 0.4f, 0.25f, 1.0f));

	// x= disruptScale, y= disruptMovementScale, z= noiseStrength, w = barScale
	m_pInterferenceParams[2]->ResetParamVec4(Vec4(0.3f, 1.0f, 0.2f, 0.8f));

	// xyz= barColor, w= barColorMultiplier
	m_pInterferenceParams[3]->ResetParamVec4(Vec4(0.39f, 0.6f, 1.0f, 5.0f));
}

////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////

void C3DHud::AddRE( const CRendElementBase *re, const SShaderItem *pShaderItem, CRenderObject *pObj)
{
	// Main thread
	const uint32 nThreadID = gcpRendD3D->m_RP.m_nFillThreadID;		
	// Only add valid render elements
	if( (SRendItem::m_RecurseLevel[nThreadID] - 1) == 0 && re && pObj && pShaderItem && pShaderItem->m_pShaderResources)         
	{
		SHudData pHudData(re, pShaderItem, (const SRenderShaderResources*)pShaderItem->m_pShaderResources, pObj);
		m_pRenderData[nThreadID].push_back( pHudData );
	}
}

////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////

void C3DHud::CalculateProjMatrix()
{
  const float fHUDFov = clamp_tpl<float>(m_pFOV->GetParam(),1.0f,180.0f);

	// Patch projection matrix to have HUD FOV
	m_mViewProj = gRenDev->m_ProjMatrix;
	float aspect = m_mViewProj.m11 / m_mViewProj.m00;
	float h = 1 / tanf(DEG2RAD(fHUDFov / 2));
	m_mViewProj.m00 = h / aspect;
	m_mViewProj.m11 = h;

	// Patch projection matrix to do x-shift by (maxParallax * (1 - hudScreenDist/w))
	m_mViewProj.m20 = -m_maxParallax;
	m_mViewProj.m30 = -m_maxParallax * CRenderer::CV_r_StereoHudScreenDist;

	m_mViewProj.Multiply(gRenDev->m_ViewMatrix, m_mViewProj);
}

////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////

void C3DHud::SetShaderParams( SHudData &pData )
{
	SRenderShaderResources *pShaderResources = (SRenderShaderResources *)pData.pShaderResources;	
	float fOpacity = pShaderResources->Opacity() * m_pOpacityMul->GetParam();

	const CRenderObject *pRO = pData.pRO;	
	Matrix44A mObjCurr, mViewProj;
	mObjCurr.Transpose(pRO->m_II.m_Matrix);
	mViewProj.Multiply(mObjCurr, m_mViewProj);
	mViewProj.Transpose();

	CShaderMan::m_sh3DHUD->FXSetVSFloat(m_pMatViewProjParamName, (Vec4 *) mViewProj.GetData(), 4);		
	
	// Since we can have multiple flash files with different resolutions we need to update resolution for correct mapping	
	Vec4 vHudTexCoordParams = Vec4(min(1.0f, (float) pData.nFlashWidth  / (float) m_pHUD_RT->GetWidth()), 
																 min(1.0f, (float) pData.nFlashHeight / (float) m_pHUD_RT->GetHeight()),
																 max(1.0f, (float) m_pHUD_RT->GetWidth() / (float) pData.s_nFlashWidthMax), 
																 max(1.0f, (float) m_pHUD_RT->GetHeight() / (float) pData.s_nFlashHeightMax));

	CShaderMan::m_sh3DHUD->FXSetVSFloat(m_pHudTexCoordParamName, &vHudTexCoordParams, 1);
	CShaderMan::m_sh3DHUD->FXSetPSFloat(m_pHudTexCoordParamName, &vHudTexCoordParams, 1);

	ColorF cDiffuse = pShaderResources->GetDiffuseColor();

	cDiffuse *= fOpacity; // pre-multiply alpha in all cases
	if (pShaderResources->m_ResFlags & MTL_FLAG_ADDITIVE)
	{
		fOpacity = 0.0f;
	}

	Vec4 vHudParams = Vec4(cDiffuse.r, cDiffuse.g, cDiffuse.b, fOpacity) * m_pHudColor->GetParamVec4();  
	CShaderMan::m_sh3DHUD->FXSetPSFloat(m_pHudParamName, &vHudParams, 1); 
}

////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////

void C3DHud::SetTextures( SHudData &pData )
{
	SEfResTexture *pDiffuse = pData.pDiffuse;
	if( pDiffuse && pDiffuse->m_Sampler.m_pTex )
		GetUtils().SetTexture(pDiffuse->m_Sampler.m_pTex, 0, FILTER_LINEAR);  
	else
		GetUtils().SetTexture(m_pHUD_RT,  0, FILTER_LINEAR);
}

////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////

void C3DHud::RenderMesh( const CRendElementBase *pRE, SShaderPass *pPass )
{
	CD3D9Renderer *const __restrict rd = gcpRendD3D;
	CREMeshImpl* pRenderMesh = (CREMeshImpl*) pRE;
	
	// Create/Update vertex buffer stream
	if( pRenderMesh->m_pRenderMesh )
  {
		pRenderMesh->m_pRenderMesh->CheckUpdate( pRenderMesh->m_pRenderMesh->_GetVertexFormat(), 0);

    // set the mesh's vertex format, which hasn't been set elsewhere.
	  rd->m_RP.m_CurVFormat = pRenderMesh->m_pRenderMesh->_GetVertexFormat();
  }

	rd->m_RP.m_pRE = const_cast<CRendElementBase*>( pRE );
	if( rd->FX_CommitStreams(pPass, true) )
	{
		rd->m_RP.m_FirstVertex = pRenderMesh->m_nFirstVertId;
		rd->m_RP.m_FirstIndex = pRenderMesh->m_nFirstIndexId;
		rd->m_RP.m_RendNumIndices = pRenderMesh->m_nNumIndices;
		rd->m_RP.m_RendNumVerts = pRenderMesh->m_nNumVerts;
		rd->m_RP.m_pRE->mfDraw(CShaderMan::m_sh3DHUD, pPass);
	}
}

////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////

void C3DHud::DownsampleHud4x4( CTexture *pDstRT )
{
	CD3D9Renderer *const __restrict rd = gcpRendD3D;
	const uint32 nThreadID = rd->m_RP.m_nProcessThreadID;

	SEfResTexture *pDiffuse = NULL;
	SShaderTechnique *pShaderTech = CShaderMan::m_sh3DHUD->mfFindTechnique(m_pDownsampleTechName);
	IF(!pShaderTech,0)
		return;

	PROFILE_LABEL_PUSH( "3D HUD DOWNSAMPLE 4X4" );

	rd->Set2DMode(true, 1, 1);	

	// Enable rendering outside backbuffer edram range
	pDstRT->SetRenderTargetTile(2); 

	rd->FX_PushRenderTarget(0,  pDstRT,  NULL); 
	rd->RT_SetViewport(0, 0, pDstRT->GetWidth(), pDstRT->GetHeight());        

	// temporary clear/fix - try avoiding this
	ColorF clearCol(0, 0, 0, 0);		
	int32 clearFlags = FRT_CLEAR_COLOR|FRT_CLEAR_IMMEDIATE;
	gcpRendD3D->EF_ClearBuffers(clearFlags, &clearCol);

	// Render in uv space - use unique geometry for discarding uncovered pixels for faster blur/downsampling
	uint32 nRECount = m_pRenderData[nThreadID].size();
	for(uint32 r = 0; r < nRECount; ++r) 
	{
		SHudData &pData = m_pRenderData[nThreadID][r];
		SRenderShaderResources *pShaderResources = (SRenderShaderResources*)pData.pShaderResources;

		pDiffuse = pData.pDiffuse;
		if( !pShaderResources || !pDiffuse || !pDiffuse->m_Sampler.m_pDynTexSource )
			continue;

		GetUtils().ShBeginPass(CShaderMan::m_sh3DHUD, m_pDownsampleTechName, FEF_DONTSETTEXTURES | FEF_DONTSETSTATES);                                   

		// Rescale uv's to match viewport
		Vec4 vTCScale = Vec4(	1, 1, (float) SHudData::s_nFlashWidthMax/(float) m_pHUD_RT->GetWidth(), (float) SHudData::s_nFlashHeightMax/(float) m_pHUD_RT->GetHeight());

		rd->SetCullMode( R_CULL_BACK );
		rd->FX_SetState( GS_NODEPTHTEST );
		
		SetShaderParams( pData );		
		SetTextures( pData );		
		RenderMesh( pData.pRE, &pShaderTech->m_Passes[0] );

		GetUtils().ShEndPass(); 
	}

	rd->FX_PopRenderTarget(0);

	rd->Set2DMode(false, 1, 1);

	pDstRT->SetRenderTargetTile(0);

	PROFILE_LABEL_POP( "3D HUD DOWNSAMPLE 4X4" );
}

////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////

void C3DHud::UpdateBloomRT( CTexture *pDstRT )
{
	CD3D9Renderer *const __restrict rd = gcpRendD3D;
	const uint32 nThreadID = rd->m_RP.m_nProcessThreadID;

	SEfResTexture *pDiffuse = NULL;
	SShaderTechnique *pShaderTech = CShaderMan::m_sh3DHUD->mfFindTechnique(m_pUpdateBloomTechName);
	IF(!pShaderTech,0)
		return;

	PROFILE_LABEL_PUSH( "UPDATE BLOOM RT" );

	// Calculate HUD's projection matrix using fixed FOV.
	CalculateProjMatrix();

	// Enable rendering outside backbuffer edram range
	pDstRT->SetRenderTargetTile(2); 

	rd->FX_PushRenderTarget(0,  pDstRT,  NULL); 
	rd->RT_SetViewport(0, 0, pDstRT->GetWidth(), pDstRT->GetHeight());        

	// temporary clear/fix - try avoiding this
	ColorF clearCol(0, 0, 0, 0);		
	int32 clearFlags = FRT_CLEAR_COLOR|FRT_CLEAR_IMMEDIATE;
	gcpRendD3D->EF_ClearBuffers(clearFlags, &clearCol);

	// Render in screen space - use unique geometry for discarding uncovered pixels for faster transfer
	uint32 nRECount = m_pRenderData[nThreadID].size();
	for(uint32 r = 0; r < nRECount; ++r) 
	{
		SHudData &pData = m_pRenderData[nThreadID][r];
		SRenderShaderResources *pShaderResources = (SRenderShaderResources*)pData.pShaderResources;

		pDiffuse = pData.pDiffuse;
		if( !pShaderResources || !pDiffuse || !pDiffuse->m_Sampler.m_pDynTexSource )
			continue;

		GetUtils().ShBeginPass(CShaderMan::m_sh3DHUD, m_pUpdateBloomTechName, FEF_DONTSETTEXTURES | FEF_DONTSETSTATES);                                   

		rd->SetCullMode( R_CULL_BACK );
		rd->FX_SetState( GS_NODEPTHTEST );

		SetShaderParams( pData );		
		//SetTextures( pData );		
		GetUtils().SetTexture(m_pHUDScaled_RT, 0, FILTER_LINEAR);    
		RenderMesh( pData.pRE, &pShaderTech->m_Passes[0] );

		GetUtils().ShEndPass(); 
	}

	rd->FX_PopRenderTarget(0);

	rd->Set2DMode(true, 1, 1);

	GetUtils().TexBlurGaussian(pDstRT, 1, 1, 0.85f, false, 0, false, CTexture::s_ptexBackBufferScaledTemp[1]);				

	rd->Set2DMode(false, 1, 1);

	rd->RT_SetViewport(0, 0, rd->GetWidth(), rd->GetHeight());

	pDstRT->SetRenderTargetTile(0);

	PROFILE_LABEL_POP( "UPDATE BLOOM RT" );
}

////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////

// Reminder: for efficient multiple flash files to work correctly - uv's must not override
void C3DHud::FlashUpdateRT(void)
{
	CD3D9Renderer *const __restrict rd = gcpRendD3D;
#if defined(USE_VBIB_PUSH_DOWN)
	//workaround for deadlock when streaming thread wants renderthread to clean mesh pool
	if(gRenDev->m_bStartLevelLoading) CRenderMesh::Tick();
#endif
	const uint32 nThreadID = rd->m_RP.m_nProcessThreadID;
	uint32 nRECount = m_pRenderData[nThreadID].size();
	
	const bool bForceRefresh = (m_pOverideCacheDelay->GetParam() > 0.5f);

	if( nRECount || bForceRefresh )//&& m_nFlashUpdateFrameID != rd->GetFrameID(false) )
	{
		// Share hud render target with scene normals
		m_pHUD_RT = CTexture::s_ptexCached3DHud;	
		m_pHUDScaled_RT = CTexture::s_ptexCached3DHudScaled;	
			
		if( (gRenDev->GetFrameID(false) % max(1, (int)CRenderer::CV_r_PostProcessHUD3DCache)) != 0 )
		{
			if( !bForceRefresh )
			{
				return;
			}
			else
			{
				m_pOverideCacheDelay->SetParam(0.0f);
				CryLog("reseting param");
			}
		} 

		rd->Set2DMode(false, 1, 1);
		m_nFlashUpdateFrameID = rd->GetFrameID(false);

		PROFILE_LABEL_PUSH( "3D HUD FLASHPLAYER UPDATES" );		
				
		SEfResTexture *pDiffuse = NULL;
		SEfResTexture *pPrevDiffuse = NULL;
		
		// Enable rendering outside backbuffer edram range
		m_pHUD_RT->SetRenderTargetTile(1);	
		rd->FX_PushRenderTarget(0,  m_pHUD_RT,  &rd->m_DepthBufferOrig);  
		
		int32 nFlashWidth = SHudData::s_nFlashWidthMax + (8 - SHudData::s_nFlashWidthMax%8);		
		int32 nFlashHeight = SHudData::s_nFlashHeightMax + (8 - SHudData::s_nFlashHeightMax%8);

		int32 nViewportWidth = min(nFlashWidth, m_pHUD_RT->GetWidth());
		int32 nViewportHeight = min(nFlashHeight, m_pHUD_RT->GetHeight());

		rd->RT_SetViewport(0, 0, nViewportWidth, nViewportHeight);     

		// temporary clear/fix - try avoiding this
		ColorF clearCol(0, 0, 0, 0);		
		int32 clearFlags = FRT_CLEAR_COLOR|FRT_CLEAR_IMMEDIATE;
		gcpRendD3D->EF_ClearBuffers(clearFlags, &clearCol);

		for(uint32 r = 0; r < nRECount; ++r) 
		{
			SHudData &pData = m_pRenderData[nThreadID][r];

			pDiffuse = pData.pDiffuse;
			if( !pDiffuse || !pDiffuse->m_Sampler.m_pDynTexSource )
				continue;

#if defined(ENABLE_FLASH_LOCKLESS_RENDERING_API)
			pDiffuse->m_Sampler.m_pDynTexSource->EnablePerFrameRendering(true);
#endif
		
			if(pData.pFlashPlayer && pPrevDiffuse != pDiffuse && pData.bVisible)
			{					
				PROFILE_LABEL_PUSH( "3D HUD FLASHFILE" );

#if !defined(ENABLE_FLASH_LOCKLESS_RENDERING_API)
				pData.pFlashPlayer->SetViewport(0, 0, min(nViewportWidth, pData.pFlashPlayer->GetWidth()), min(nViewportHeight, pData.pFlashPlayer->GetHeight()));
#else
				((IDynTextureSourceImpl*)pDiffuse->m_Sampler.m_pDynTexSource)->SetSize(min(nViewportWidth, pData.pFlashPlayer->GetWidth()), min(nViewportHeight, pData.pFlashPlayer->GetHeight()));
#endif
				pData.pFlashPlayer->AvoidStencilClear(!rd->CV_r_PostProcessHUD3DStencilClear);

				rd->FX_Commit(true);

#if defined(ENABLE_FLASH_LOCKLESS_RENDERING_API)
				pData.pFlashPlayer->RenderPlaybackLockless(false, false);
#else
				pData.pFlashPlayer->Render(false);
#endif

				rd->FX_SetState(gcpRendD3D->m_RP.m_CurState & ~GS_BLEND_MASK);

				pData.pFlashPlayer->AvoidStencilClear(false);

				pPrevDiffuse = pDiffuse;
				
				PROFILE_LABEL_POP( "3D HUD FLASHFILE" );

 				break;
			}
		}







		rd->FX_PopRenderTarget(0);
		m_pHUD_RT->SetRenderTargetTile(0);

		// Downsample/blur hud into half res target _1 time only_ - we'll use this for Bloom/Dof
		DownsampleHud4x4( m_pHUDScaled_RT );

		rd->Set2DMode(true, 1, 1);
		// Since we caching results, lets do a bit better blur
		
		PROFILE_LABEL_POP( "3D HUD FLASHPLAYER UPDATES" );		
	}
}

////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////

void C3DHud::RenderFinalPass()
{
	CD3D9Renderer *const __restrict rd = gcpRendD3D;

	PROFILE_LABEL_PUSH( "3D HUD FINAL PASS" );	

	const uint32 nThreadID = rd->m_RP.m_nProcessThreadID;
	SShaderTechnique *pShaderTech = CShaderMan::m_sh3DHUD->mfFindTechnique(m_pGeneralTechName);
	IF(!pShaderTech,0)
		return;

	rd->Set2DMode(false, 1, 1);

	UpdateBloomRT( CTexture::s_ptexBackBufferScaled[1] );

	SEfResTexture *pDiffuse = NULL;
	SEfResTexture *pPrevDiffuse = NULL;

  // Calculate HUD's projection matrix using fixed FOV.
  CalculateProjMatrix();

	// Hud simple 2D dof blend
	CPostEffect *pDofPostEffect = PostEffectMgr()->GetEffect(ePFX_eDepthOfField);
	bool bGameDof = pDofPostEffect->IsActive();

	static float fDofBlend = 0.0f;
  fDofBlend += ((bGameDof? .6f:0.0f) - fDofBlend) * gEnv->pTimer->GetFrameTime() * 10.0f; 
	float dofMultiplier = clamp_tpl<float>(m_pDofMultiplier->GetParam(), 0.0f, 2.0f);
	float fCurrentDofBlend = fDofBlend * dofMultiplier;

	bool bInterferenceApplied = false;
	uint32 hudEffectParamCount = 1; // Default to only pass 1 vec
	Vec4 vHudEffectParams[5]; // [0] = default params, [1]-[4] = interference params
	Vec4 vInterferenceParams;
	float interferenceStrength = clamp_tpl<float>(m_pInterference->GetParam(),0.0f,1.0f);
	if(interferenceStrength > 0.0f)
	{
		bInterferenceApplied = true;
		gcpRendD3D->m_RP.m_FlagsShader_RT |= g_HWSR_MaskBit[HWSR_SAMPLE1]; // Set interference combination
		hudEffectParamCount+=4; // Pass interference params as well

		// x= hudItemOverrideStrength, y= interferenceRandFrequency, z= dofInterferenceStrength, w = free
		vInterferenceParams = m_pInterferenceParams[0]->GetParamVec4();
		Limit(vInterferenceParams.x,0.0f,1.0f);
		Limit(vInterferenceParams.y,0.0f,1.0f);
		Limit(vInterferenceParams.z,0.0f,1.0f);

		float interferenceRandFrequency = max(vInterferenceParams.y,0.0f);
		if(m_interferenceRandTimer >= interferenceRandFrequency)
		{
			m_interferenceRandNums.x = RandomNum();
			m_interferenceRandNums.y = RandomNum();
			m_interferenceRandNums.z = UnsRandomNum();
			m_interferenceRandNums.w = RandomNum();
			m_interferenceRandTimer = 0.0f;
		}

		// x = randomGrainStrengthScale, y= randomFadeStrengthScale, z= chromaShiftStrength, w= chromaShiftDist
		const float chromaShiftMaxStrength = 2.0f;
		const float chromaShiftMaxDist = 0.007f;
		vHudEffectParams[2] = m_pInterferenceParams[1]->GetParamVec4();
		Limit(vHudEffectParams[2].x,0.0f,1.0f);
		Limit(vHudEffectParams[2].y,0.0f,1.0f);
		vHudEffectParams[2].x = ((1.0f-vHudEffectParams[2].x) + (m_interferenceRandNums.z * vHudEffectParams[2].x));
		vHudEffectParams[2].y = ((1.0f-vHudEffectParams[2].y) + (m_interferenceRandNums.z*vHudEffectParams[2].y));
		vHudEffectParams[2].z = clamp_tpl<float>(vHudEffectParams[2].z,0.0f,1.0f) * chromaShiftMaxStrength;
		vHudEffectParams[2].w = clamp_tpl<float>(vHudEffectParams[2].w,0.0f,1.0f) * chromaShiftMaxDist;

		// x= disruptScale, y= disruptMovementScale, z= noiseStrength, w = barScale		
		const float disruptMaxScale = 100.0f;
		const float disruptMovementMaxScale = 0.012f;
		const float noiseMaxScale = 10.0f;
		const float barMaxScale = 10.0f;
		vHudEffectParams[3] = m_pInterferenceParams[2]->GetParamVec4();
		vHudEffectParams[3].x = clamp_tpl<float>(vHudEffectParams[3].x,0.0f,1.0f) * disruptMaxScale;
		vHudEffectParams[3].y = clamp_tpl<float>(vHudEffectParams[3].y,0.0f,1.0f) * disruptMovementMaxScale;
		vHudEffectParams[3].z = clamp_tpl<float>(vHudEffectParams[3].z,0.0f,1.0f) * noiseMaxScale;
		Limit(vHudEffectParams[3].w,0.0f,1.0f);
		vHudEffectParams[3].x *= m_interferenceRandNums.z * m_interferenceRandNums.z;
		vHudEffectParams[3].w = (1.0f - vHudEffectParams[3].w) * m_interferenceRandNums.z * barMaxScale;

		// xyz= barColor, w= bar color scale
		vHudEffectParams[4] = m_pInterferenceParams[3]->GetParamVec4();
		Limit(vHudEffectParams[4].x,0.0f,1.0f);
		Limit(vHudEffectParams[4].y,0.0f,1.0f);
		Limit(vHudEffectParams[4].z,0.0f,1.0f);
		Limit(vHudEffectParams[4].w,0.0f,20.0f);
		vHudEffectParams[4] *= vHudEffectParams[4].w; // Scale color values by color scale
		vHudEffectParams[4].w = vInterferenceParams.z; // dofInterferenceStrength
	}

#if !defined(_RELEASE) && !defined(CONSOLE_CONST_CVAR_MODE)
	// Turn on debug view in shader
	if(m_bDebugView)
	{
		gcpRendD3D->m_RP.m_FlagsShader_RT |= g_HWSR_MaskBit[HWSR_DEBUG1];
	}
	bool bDrawingSolidView = (CRenderer::CV_r_PostProcessHUD3DDebugView == eHUD3D_DEBUG_VIEW_SolidFill);
	if(m_bWireframeEnabled || bDrawingSolidView)
	{
		gcpRendD3D->m_RP.m_FlagsShader_RT |= g_HWSR_MaskBit[HWSR_DEBUG2];
	}
	// Rand num generator used to color each hud item
	CMTRand_int32 rndGen;
#endif

	uint32 nRECount = m_pRenderData[nThreadID].size();
	for(uint32 r = 0; r < nRECount; ++r) 
	{
		SHudData &pData = m_pRenderData[nThreadID][r];
		SRenderShaderResources *pShaderResources = (SRenderShaderResources*)pData.pShaderResources;

		pDiffuse = pData.pDiffuse;
		if( !pShaderResources || !pDiffuse || !pDiffuse->m_Sampler.m_pDynTexSource )
			continue;

		SRenderObjData* pROData = pData.pRO->GetObjData(nThreadID);

		int32 nRenderState = GS_BLSRC_ONE | GS_BLDST_ONEMINUSSRCALPHA;
		if (!(pROData && pROData->m_nCustomFlags & COB_HUD_REQUIRE_DEPTHTEST))
		{
			nRenderState |= GS_NODEPTHTEST;
		}

		GetUtils().ShBeginPass(CShaderMan::m_sh3DHUD, m_pGeneralTechName, FEF_DONTSETTEXTURES | FEF_DONTSETSTATES);                                   
  
		rd->SetCullMode( R_CULL_BACK );

		SetShaderParams( pData );
    
		// Set additional parameters
		vHudEffectParams[0].x = fCurrentDofBlend;
		
		// baking constant glow out of shader
		const float fGlowConstScale = CRenderer::CV_r_PostProcessHUD3DGlowAmount; // tweaked constant - before was 2.0, but barely visible due to hud shadow
		vHudEffectParams[0].y = pShaderResources->Glow() * m_pGlowMul->GetParam() * fGlowConstScale;
		vHudEffectParams[0].z = interferenceStrength;
		
		// Apply filter if ignore flag not set
		if(pROData && (pROData->m_nCustomData ^ COB_IGNORE_HUD_INTERFERENCE_FILTER))
		{
			vHudEffectParams[0].z *= vInterferenceParams.x;
		}

		// baking out of shader shadow amount const multiplication
		const float fShadowAmountDotConst = 0.33f;
		vHudEffectParams[0].w = CRenderer::CV_r_PostProcessHUD3DShadowAmount * fShadowAmountDotConst;

		if(bInterferenceApplied)
		{
			float vTCScaleX = (float) SHudData::s_nFlashWidthMax/(float) m_pHUD_RT->GetWidth() * 2.0f;
			float vTCScaleY = (float) SHudData::s_nFlashHeightMax/(float) m_pHUD_RT->GetHeight() * 2.0f;

			vHudEffectParams[1].x = m_interferenceRandNums.x * vTCScaleX;
			vHudEffectParams[1].y = m_interferenceRandNums.y * vTCScaleY;
			vHudEffectParams[1].z = 0.0f;
			vHudEffectParams[1].w = m_interferenceRandNums.w;

			GetUtils().SetTexture(m_pNoise, 2, FILTER_POINT, TADDR_WRAP);
		}

#if !defined(_RELEASE) && !defined(CONSOLE_CONST_CVAR_MODE)
		// Debug settings
		if(m_bWireframeEnabled || bDrawingSolidView)
		{
			nRenderState = GS_BLSRC_ONE | GS_BLDST_ZERO;
			if (!(pROData && pROData->m_nCustomFlags & COB_HUD_REQUIRE_DEPTHTEST))
			{
				nRenderState |= GS_NODEPTHTEST;
			}

			// Turn on wireframe
			if(m_bWireframeEnabled)
			{
				nRenderState |= GS_WIREFRAME;
			}
			// Set color of wire frame and solid fill debug views
			vHudEffectParams[0].x = (rndGen.GenerateFloat()*0.75f) + 0.25f;
			vHudEffectParams[0].y = (rndGen.GenerateFloat()*0.75f) + 0.25f;
			vHudEffectParams[0].z = (rndGen.GenerateFloat()*0.75f) + 0.25f;
			vHudEffectParams[0].w = 1.0f;
		}
#endif

		CShaderMan::m_sh3DHUD->FXSetPSFloat(m_pHudEffectsParamName, vHudEffectParams, hudEffectParamCount); 

		rd->FX_SetState( nRenderState );

		SetTextures( pData );		
		GetUtils().SetTexture(CTexture::s_ptexBackBufferScaled[1], 1, FILTER_LINEAR);    

		RenderMesh( pData.pRE, &pShaderTech->m_Passes[0] );

		GetUtils().ShEndPass(); 

		pPrevDiffuse = pDiffuse;
	}

	rd->Set2DMode(true, 1, 1);

	PROFILE_LABEL_POP( "3D HUD FINAL PASS" );
};

////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////

void C3DHud::FinalPass()
{
	RenderFinalPass();

	// Debug views
#if !defined(_RELEASE) && !defined(CONSOLE_CONST_CVAR_MODE)
	if(CRenderer::CV_r_PostProcessHUD3DDebugView == eHUD3D_DEBUG_VIEW_UnwrapOnToFlashTex)
	{
		// Draw flash RT to screen for debugging
		PostProcessUtils().ShBeginPass(CShaderMan::m_shPostEffects, m_pTexToTexTechName, FEF_DONTSETTEXTURES | FEF_DONTSETSTATES); 
		gcpRendD3D->FX_SetState(GS_NODEPTHTEST | GS_BLSRC_ONE | GS_BLDST_ZERO);
		PostProcessUtils().SetTexture(m_pHUD_RT, 0, FILTER_LINEAR);  
		PostProcessUtils().DrawFullScreenQuad(CTexture::s_ptexBackBuffer->GetWidth(), CTexture::s_ptexBackBuffer->GetHeight()); 
		PostProcessUtils().ShEndPass(); 
	}

	m_bDebugView = (CRenderer::CV_r_PostProcessHUD3DDebugView == eHUD3D_DEBUG_VIEW_UnwrapOnToFlashTex);
	m_bWireframeEnabled = (CRenderer::CV_r_PostProcessHUD3DDebugView == eHUD3D_DEBUG_VIEW_WireFrame) || 
												(CRenderer::CV_r_PostProcessHUD3DDebugView == eHUD3D_DEBUG_VIEW_UnwrapOnToFlashTex);

	if(m_bDebugView || m_bWireframeEnabled)
	{
		// Draw debug views
		RenderFinalPass();

		m_bDebugView = false;
		m_bWireframeEnabled = false;
	}
#endif
}

////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////

void C3DHud::Render()
{
	PROFILE_SHADER_START
	GPU_TIMER_START( "3D_HUD" );
	PROFILE_LABEL_PUSH( "3D HUD" );

	gcpRendD3D->m_RP.m_FlagsShader_RT &= ~(	g_HWSR_MaskBit[HWSR_SAMPLE0]|
																					g_HWSR_MaskBit[HWSR_SAMPLE1]|
																					g_HWSR_MaskBit[HWSR_SAMPLE2]|
																					g_HWSR_MaskBit[HWSR_DEBUG1]|
																					g_HWSR_MaskBit[HWSR_DEBUG2]);

	CD3DStereoRenderer &pS3DRend = gcpRendD3D->GetS3DRend();
	bool bPostProcStereo = pS3DRend.IsPostStereoEnabled();

	// If post-stereo not enabled, update flash player
	if( !bPostProcStereo )
		FlashUpdateRT(); 
	
	// Update interference rand timer
	m_interferenceRandTimer += gEnv->pTimer->GetFrameTime();

	// Render hud with projection offset or with same projection offset in MRT mode (a bit faster)
	if( bPostProcStereo )
	{		
		if( CRenderer::CV_r_StereoHudScreenDist )
		{
			float maxParallax = 0.005f * pS3DRend.GetStereoStrength();

			// Render left eye
			pS3DRend.BeginRenderingTo(LEFT_EYE);		
			m_maxParallax = -maxParallax;
			FinalPass();    
			pS3DRend.EndRenderingTo(LEFT_EYE);
					
			// Render right eye
			pS3DRend.BeginRenderingTo(RIGHT_EYE);			
			m_maxParallax = maxParallax;
			FinalPass();
			pS3DRend.EndRenderingTo(RIGHT_EYE);
			
			m_maxParallax = 0;
		}
		else
		{
			pS3DRend.BeginRenderingMRT(false);
			gcpRendD3D->m_RP.m_FlagsShader_RT |= g_HWSR_MaskBit[HWSR_SAMPLE0];
			FinalPass();
			pS3DRend.EndRenderingMRT();
		}
	}
	else
	{
		FinalPass();
	}
	
	PROFILE_LABEL_POP( "3D HUD" );
	GPU_TIMER_STOP( "3D_HUD" );

  gcpRendD3D->FX_Flush(); 
  PROFILE_SHADER_END  
}
